// system.cc 
//	Nachos initialization and cleanup routines.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"

// This defines *all* of the global data structures used by Nachos.
// These are all initialized and de-allocated by this file.

Thread *currentThread; // the thread we are running now
Thread *threadToBeDestroyed; // the thread that just finished
Scheduler *scheduler; // the ready list
Interrupt *interrupt; // interrupt status
Statistics *stats; // performance metrics
Timer *timer; // the hardware timer device,
// for invoking context switches

#ifdef FILESYS_NEEDED
FileSystem *fileSystem;
#endif

#ifdef FILESYS
SynchDisk *synchDisk;
#endif


#ifdef USER_PROGRAM	// requires either FILESYS or FILESYS_STUB
Machine *machine; // user program memory and registers
ProcessManager *pm;
MemoryManager *memManager;
SysOpenFileManager* fileManager;
char diskBuffer[PageSize];
VirtualMemoryManager *vmmanager;
/*
 int UserReadWrite(OpenFile *file, int size, int fileAddr, int type) {
 ASSERT(size<=PageSize);
 if (type == USER_READ) {
 return file->ReadAt(diskBuffer, size, fileAddr);
 } else if (type == USER_WRITE) {
 return file->WriteAt(diskBuffer, size, fileAddr);
 }
 }
 int ReadWrite(int virAddr, OpenFile *file, int size, int fileAddr, int type) {
 int bytesMoved = 0;
 int physicAddr;
 int bytesToCopy;
 int pageSizeLeft;
 if (type == USER_READ) {
 while (size > 0) {
 int toRead = min(size,PageSize);
 int bytes = UserReadWrite(file, toRead, fileAddr, USER_READ);
 int bytesCopied = 0;
 bytesToCopy = bytes;
 while (bytesToCopy > 0) {
 machine->Translate(virAddr, &physicAddr, bytesToCopy, FALSE);
 pageSizeLeft = PageSize - (physicAddr) % PageSize;
 int actualToCopy = min(pageSizeLeft,bytesToCopy);
 bcopy(diskBuffer+bytesCopied, machine->mainMemory + physicAddr,
 actualToCopy);
 bytesCopied+=actualToCopy;
 bytesToCopy -= actualToCopy;
 virAddr += actualToCopy;
 bytesMoved += actualToCopy;
 }
 size -= bytes;
 fileAddr += bytes;
 if (bytes < toRead)
 return bytesMoved;
 }
 } else if (type == USER_WRITE) {
 while (size > 0) {
 int bytesCopied = 0;
 bytesToCopy = min(size,PageSize);
 while (bytesCopied < bytesToCopy) {
 machine->Translate(virAddr, &physicAddr, bytesToCopy, FALSE);
 pageSizeLeft = PageSize - (physicAddr) % PageSize;
 int actualToCopy = min(pageSizeLeft,bytesToCopy-bytesCopied);
 bcopy(machine->mainMemory + physicAddr, diskBuffer+bytesCopied,actualToCopy);
 bytesCopied += actualToCopy;
 virAddr += actualToCopy;
 bytesMoved += actualToCopy;
 }
 int bytes = UserReadWrite(file, bytesToCopy, fileAddr, USER_WRITE);
 size -= bytes;
 fileAddr += bytes;
 if (bytes < bytesToCopy)
 return bytesMoved;
 }
 }
 return bytesMoved;
 }
 */

int ReadWrite(int virAddr, char* buffer, int size, int type) {
	int physicAddr;
	int pageSizeLeft;
	int bytesCopied = 0;
	ExceptionType exception;
	if (type == USER_READ) {
		while (size > 0) {
			do {
				exception = machine->Translate(virAddr, &physicAddr, size, FALSE);
				if (exception != NoException) {
					machine->RaiseException(exception, virAddr);
				}
			}while(exception != NoException);
			pageSizeLeft = PageSize - (physicAddr) % PageSize;
			int actualToCopy = min(pageSizeLeft,size);
			bcopy(buffer + bytesCopied, machine->mainMemory + physicAddr,
					actualToCopy);
			size -= actualToCopy;
			bytesCopied += actualToCopy;
			virAddr += actualToCopy;
		}
	} else if (type == USER_WRITE) {
		while (size > 0) {
			do {
				exception = machine->Translate(virAddr, &physicAddr, size, FALSE);
				if (exception != NoException) {
					machine->RaiseException(exception, virAddr);
				}
			}while(exception != NoException);

			pageSizeLeft = PageSize - (physicAddr) % PageSize;
			int actualToCopy = min(pageSizeLeft,size);
			bcopy( machine->mainMemory + physicAddr,buffer + bytesCopied,actualToCopy);
			size -= actualToCopy;
			bytesCopied += actualToCopy;
			virAddr += actualToCopy;
		}
	}
	return bytesCopied;
}

#endif

#ifdef NETWORK
PostOffice *postOffice;
#endif

// External definition, to allow us to take a pointer to this function
extern void Cleanup();

//----------------------------------------------------------------------
// TimerInterruptHandler
// 	Interrupt handler for the timer device.  The timer device is
//	set up to interrupt the CPU periodically (once every TimerTicks).
//	This routine is called each time there is a timer interrupt,
//	with interrupts disabled.
//
//	Note that instead of calling Yield() directly (which would
//	suspend the interrupt handler, not the interrupted thread
//	which is what we wanted to context switch), we set a flag
//	so that once the interrupt handler is done, it will appear as 
//	if the interrupted thread called Yield at the point it is 
//	was interrupted.
//
//	"dummy" is because every interrupt handler takes one argument,
//		whether it needs it or not.
//----------------------------------------------------------------------
static void TimerInterruptHandler(int dummy) {
	if (interrupt->getStatus() != IdleMode)
		interrupt->YieldOnReturn();
}

//----------------------------------------------------------------------
// Initialize
// 	Initialize Nachos global data structures.  Interpret command
//	line arguments in order to determine flags for the initialization.  
// 
//	"argc" is the number of command line arguments (including the name
//		of the command) -- ex: "nachos -d +" -> argc = 3 
//	"argv" is an array of strings, one for each command line argument
//		ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
//----------------------------------------------------------------------
void Initialize(int argc, char **argv) {
	int argCount;
	char* debugArgs = "";
	bool randomYield = FALSE;

#ifdef USER_PROGRAM
	bool debugUserProg = FALSE; // single step user program
#endif
#ifdef FILESYS_NEEDED
	bool format = FALSE; // format disk
#endif
#ifdef NETWORK
	double rely = 1; // network reliability
	int netname = 0; // UNIX socket name
#endif

	for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount) {
		argCount = 1;
		if (!strcmp(*argv, "-d")) {
			if (argc == 1)
				debugArgs = "+"; // turn on all debug flags
			else {
				debugArgs = *(argv + 1);
				argCount = 2;
			}
		} else if (!strcmp(*argv, "-rs")) {
			ASSERT(argc > 1);
			RandomInit(atoi(*(argv + 1))); // initialize pseudo-random
			// number generator
			randomYield = TRUE;
			argCount = 2;
		}
#ifdef USER_PROGRAM
		if (!strcmp(*argv, "-s"))
		debugUserProg = TRUE;
#endif
#ifdef FILESYS_NEEDED
		if (!strcmp(*argv, "-f"))
		format = TRUE;
#endif
#ifdef NETWORK
		if (!strcmp(*argv, "-l")) {
			ASSERT(argc > 1);
			rely = atof(*(argv + 1));
			argCount = 2;
		} else if (!strcmp(*argv, "-m")) {
			ASSERT(argc > 1);
			netname = atoi(*(argv + 1));
			argCount = 2;
		}
#endif
	}

	DebugInit(debugArgs); // initialize DEBUG messages
	stats = new Statistics(); // collect statistics
	interrupt = new Interrupt; // start up interrupt handling
	scheduler = new Scheduler(); // initialize the ready queue
	if (randomYield) // start the timer (if needed)
		timer = new Timer(TimerInterruptHandler, 0, randomYield);

	threadToBeDestroyed = NULL;

	// We didn't explicitly allocate the current thread we are running in.
	// But if it ever tries to give up the CPU, we better have a Thread
	// object to save its state.
	currentThread = new Thread("main");
	currentThread->setStatus(RUNNING);

	interrupt->Enable();
	CallOnUserAbort(Cleanup); // if user hits ctl-C
#ifdef USER_PROGRAM
	machine = new Machine(debugUserProg); // this must come first
	pm = new ProcessManager();
	memManager = new MemoryManager();
	fileManager = new SysOpenFileManager();
#endif

#ifdef FILESYS
	synchDisk = new SynchDisk("DISK");
#endif

#ifdef FILESYS_NEEDED
	fileSystem = new FileSystem(format);
#endif

#ifdef NETWORK
	postOffice = new PostOffice(netname, rely, 10);
#endif
#ifdef USER_PROGRAM
	vmmanager = new VirtualMemoryManager();
#endif
}

//----------------------------------------------------------------------
// Cleanup
// 	Nachos is halting.  De-allocate global data structures.
//----------------------------------------------------------------------
void Cleanup() {
	printf("\nCleaning up...\n");
#ifdef NETWORK
	delete postOffice;
#endif

#ifdef USER_PROGRAM
	delete machine;
	delete pm;
	delete memManager;
	delete fileManager;
	delete vmmanager;
#endif

#ifdef FILESYS_NEEDED
	delete fileSystem;
#endif

#ifdef FILESYS
	delete synchDisk;
#endif

	delete timer;
	delete scheduler;
	delete interrupt;

	Exit(0);
}

